const WebSocket = require('ws');
const express = require('express');
const http = require('http');
const url = require('url');
const querystring = require('querystring');
const config = require('./config.js');
const fs = require('fs');
const path = require('path');

// 创建Express应用
const app = express();
// 限制请求体大小
app.use(express.json({ limit: '3000kb' }));
app.use(express.urlencoded({ extended: true }));

// 创建HTTP服务器
const server = http.createServer(app);

// 创建WebSocket服务器
const wss = new WebSocket.Server({ noServer: true });

// 存储设备连接的映射 (设备编号 -> WebSocket连接)
const deviceConnections = new Map();

// 存储等待设备响应的请求
const pendingMessages = new Map(); // messageId -> { resolve, reject, timeout }

// 处理HTTP升级到WebSocket
server.on('upgrade', (request, socket, head) => {
  const pathname = url.parse(request.url).pathname;

  // 设备WebSocket连接路径: /device (不再包含设备编号参数)
  if (pathname === '/') {
    wss.handleUpgrade(request, socket, head, (ws) => {
      wss.emit('connection', ws, request);
    });
  } else {
    socket.write('HTTP/1.1 404 Not Found\r\n\r\n');
    socket.destroy();
  }
});

// 处理WebSocket连接
wss.on('connection', (ws, request) => {
  let deviceSn = null; // 设备编号，将通过客户端声明获取
  console.log('新的WebSocket连接已建立，等待客户端声明...');

  // 处理设备发送的消息
  ws.on('message', (data) => {
    try {
      const message = JSON.parse(data.toString());
      console.log('收到设备消息:', message);
      writeLog('RECEIVE', message);

      // 处理客户端声明
      if (message.cmd === 'declare' && message.type === 'device') {
        deviceSn = message.sn; // 从声明中获取设备编号
        deviceConnections.set(deviceSn, ws);
        console.log(`设备 ${deviceSn} 已通过客户端声明验证并注册`);
        return;
      }

      // 处理设备发送的心跳包
      if (message.cmd === 'ping') {
        console.log(`收到设备 ${deviceSn} 的心跳包`);
        ws.send(JSON.stringify({ cmd: 'pong' }));
        return;
      }

      // 处理设备响应消息
      if (message.to && pendingMessages.has(message.to)) {
        const pending = pendingMessages.get(message.to);
        clearTimeout(pending.timeout);
        pending.resolve(message);
        pendingMessages.delete(message.to);
        console.log(`设备 ${deviceSn} 响应消息:`, message.data);
        return;
      }

      // 其他消息处理
      console.log(`设备 ${deviceSn} 发送的其他消息:`, message);
    } catch (error) {
      console.error('解析设备消息失败:', error);
    }
  });

  // 处理连接关闭
  ws.on('close', () => {
    if (deviceSn) {
      console.log(`设备 ${deviceSn} 已断开连接`);
      deviceConnections.delete(deviceSn);
    } else {
      console.log('未完成客户端声明的WebSocket连接已断开');
    }
  });

  // 处理错误
  ws.on('error', (error) => {
    console.error(`设备 ${deviceSn || '未知'} 连接错误:`, error);
  });
});

/**
 * HTTP API端点，用于接收转发请求
 * 示例请求: POST /api/send/{设备编号}
 * 请求体: {
 *   "command": "addUser",
 *   "data": {
 *     "user_id": "用户ID",
 *     "name": "用户名",
 *     ...
 *   }
 * }
 */
app.post('/api/send/:deviceSn', async (req, res) => {
  try {
    const { deviceSn } = req.params;
    const { cmd, data } = req.body;

    if (!deviceSn || !cmd || !data) {
      return res.status(400).json({
        code: -1,
        message: '缺少必要参数: deviceSn (URL路径), cmd, data'
      });
    }

    // 获取设备连接
    const ws = deviceConnections.get(deviceSn);
    if (!ws || ws.readyState !== WebSocket.OPEN) {
      return res.status(404).json({
        code: -2,
        message: `设备 ${deviceSn} 未连接或连接已关闭`
      });
    }

    // 生成消息ID
    const messageId = `msg_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;

    try {
      // 发送消息并等待设备响应
      const deviceResponse = await new Promise((resolve, reject) => {
        // 设置超时定时器 (30秒)
        const timeout = setTimeout(() => {
          pendingMessages.delete(messageId);
          reject(new Error('设备响应超时'));
        }, config.server.responseTimeout);

        // 存储等待响应的请求
        pendingMessages.set(messageId, {
          resolve,
          reject,
          timeout
        });

        // 构建WebSocket消息格式
        const wsMessage = {
          cmd: 'to_device',
          from: messageId,
          to: deviceSn,
          data: {
            cmd: cmd,
            ...data
          }
        };

        // 发送消息
        ws.send(JSON.stringify(wsMessage), (error) => {
          if (error) {
            clearTimeout(timeout);
            pendingMessages.delete(messageId);
            reject(new Error('消息发送失败: ' + error.message));
          } else {
            writeLog('SEND', wsMessage);
          }
        });
      });

      // 设备响应成功
      res.json({ 
        code: 0,
        message: '设备已响应',
        data: deviceResponse
      });
      writeLog('HTTP_RESPONSE', { url: req.url, response: { code: 0, message: '设备已响应', data: deviceResponse } });
    } catch (error) {
      // 处理错误
      res.status(500).json({
        code: -3,
        message: error.message
      });
    }
  } catch (error) {
    console.error('处理API请求失败:', error);
    res.status(500).json({
      code: -500,
      message: '服务器内部错误'
    });
  }
});

// 健康检查接口
app.get('/health', (req, res) => {
  const connectedDevices = Array.from(deviceConnections.keys());
  res.json({
    status: 'ok',
    deviceCount: deviceConnections.size,
    devices: connectedDevices,
    timestamp: Date.now()
  });
});

// 启动服务器
const PORT = config.server.port;
server.listen(PORT, () => {
  console.log(`中转服务已启动，监听端口 ${PORT}`);
  console.log(`WebSocket设备连接地址: ws://localhost:${PORT}`);
  console.log(`HTTP API地址: http://localhost:${PORT}/api/send/{设备编号}`);
});

// 处理未捕获的异常
process.on('uncaughtException', (error) => {
  console.error('未捕获的异常:', error);
});

process.on('unhandledRejection', (reason, promise) => {
  console.error('未处理的Promise拒绝:', reason, promise);
});

// 日志初始化
const logDir = path.join(__dirname, 'log');
const logFileName = new Date().toISOString().replace(/[^0-9]/g, '').slice(0, 14) + '.log';
const logFilePath = path.join(logDir, logFileName);

// 创建log目录
if (!fs.existsSync(logDir)) {
  fs.mkdirSync(logDir, { recursive: true });
}

// 创建日志文件
fs.writeFileSync(logFilePath, `[${new Date().toISOString()}] 服务启动\n`);

// 日志轮转 - 保留最多5个日志文件
const logFiles = fs.readdirSync(logDir)
  .filter(file => file.endsWith('.log'))
  .map(file => ({
    name: file,
    path: path.join(logDir, file),
    ctime: fs.statSync(path.join(logDir, file)).ctime
  }))
  .sort((a, b) => b.ctime - a.ctime); // 按创建时间降序排列

if (logFiles.length > 5) {
  const filesToDelete = logFiles.slice(5);
  filesToDelete.forEach(file => {
    fs.unlinkSync(file.path);
    console.log(`删除旧日志文件: ${file.name}`);
  });
}

// 日志写入函数
function writeLog(type, message) {
  const logEntry = `[${new Date().toISOString()}] [${type}] ${JSON.stringify(message)}\n`;
  try {
    fs.appendFileSync(logFilePath, logEntry);
  } catch (error) {
    console.error('写入日志失败:', error);
  }
}